import javax.swing.*;
import javax.swing.plaf.FontUIResource;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class GomokuGame {   //主类
    public static void main(String[] args) {
        setUIFont(new javax.swing.plaf.FontUIResource("微软雅黑", Font.PLAIN,22));

        new GomokuGame();
    }

    public static void setUIFont(FontUIResource f){     //是中文显示的方法
        java.util.Enumeration<Object> keys = UIManager.getDefaults().keys();
        while (keys.hasMoreElements()){
            Object key = keys.nextElement();
            Object value = UIManager.get(key);
            if (value instanceof javax.swing.plaf.FontUIResource){
                UIManager.put(key, f);
            }
        }
    }

    public GomokuGame(){    //方法
        JFrame frame = new JFrame("禁手五子棋？？！");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   //关闭
        frame.setSize(675,800);
        frame.setLocationRelativeTo(null);      //居中

        GomokuPanel panel = new GomokuPanel();      //游戏面板
        frame.add(panel, BorderLayout.CENTER);      //面板居中

        JPanel bottomPanel = new JPanel();  // 创建底部重开面板

        JButton resetButton = new JButton("重新开始");  // 创建重开按钮
        resetButton.addActionListener(new ActionListener() {    //按钮效果
            @Override
            public void actionPerformed(ActionEvent e) {
                panel.initializeGame(); //游戏面板使用初始化
            }
        });
        bottomPanel.add(resetButton);

        JButton ruleButton = getJButton(frame);
        bottomPanel.add(ruleButton);

        frame.add(bottomPanel, BorderLayout.SOUTH);   //底部,中间会遮住游戏面板
        frame.setVisible(true);     //可见
    }

    private  JButton getJButton(JFrame frame) {
        JButton ruleButton = new JButton("禁手规则");
        ruleButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(frame,
                        "禁手规则（仅对黑子有效）:\n" +
                                "1. 三三禁手：黑子一子落下同时形成两个或两个以上的活三\n" +
                                "2. 四四禁手：黑子一子落下同时形成两个或两个以上的四子\n" +
                                "3. 长连禁手：黑子一子落下形成六个或六个以上的连续棋子\n" +
                                "违反以上任何禁手规则，黑子理论上判负",
                        "禁手规则说明", JOptionPane.INFORMATION_MESSAGE);
                //JOptionPane.INFORMATION_MESSAGE是一种显示格式
            }
        });
        return ruleButton;
    }
}


class GomokuPanel extends JPanel {
    private final int board_Size = 15;      //格子数
    private int cell_Size = 40;      //格子大小
    private int player_Size = 36;
    private int margin = 50;    //边距,用于坐标转换

    private  int[][] board; //0:空 1:黑 2:白
    private boolean player_black;    //ture=black回合
    private boolean GameOver;
    private boolean Forbidden_hands = true;    //禁手

    public GomokuPanel(){   //方法
        initializeGame();   //使用初始化

        this.addMouseListener(new MouseAdapter() {  //鼠标操作
            @Override
            public void mouseClicked(MouseEvent e) {
                if (GameOver){
                    return;
                }

                int x = e.getX();
                int y = e.getY();
//                x - margin
//                减去边缘留白，得到相对于棋盘内部的坐标
//                + cell_Size / 2
//                加上半个格子的大小，这是为了四舍五入
//                例如：点击位置在格子左半部分算作前一个格子，右半部分算作后一个格子
//                / cell_Size
//                除以每个格子的大小，得到格子索引（从0开始）
                int col = (x - margin + cell_Size / 2) / cell_Size; //列
                int row = (y - margin + cell_Size / 2) / cell_Size; //行

                if (row >= 0 && row < board_Size && col >= 0 && col <board_Size && board[row][col] == 0){
                    board[row][col] = player_black ? 1:2;

                    if (player_black && Forbidden_hands && isForbidden_hands(row, col)) {
                        board[row][col] = 0;
                        JOptionPane.showMessageDialog(null, "黑棋禁手，请重新落子！");
                    } else if (checkWin(row, col)){    //胜利条件判断
                        GameOver = true;
                        String  winner = player_black ? "黑棋" : "白棋";
                        JOptionPane.showMessageDialog(null, winner + "胜利!");
                    } else if (isBoardFull()) {
                        GameOver = true;
                        JOptionPane.showMessageDialog(null,"棋盘已满，平局!");
                    } else {
                        player_black = !player_black;  //切换回合
                    }

                    repaint();  //重绘
                }
            }
        });
    }

    private boolean isBoardFull(){      //方法,判断平局
        for (int row = 0; row < board_Size; row ++){
            for ( int col = 0 ; col < board_Size ; col++){
                if (board[row][col] == 0){
                    return  false;  //有空位
                }
            }
        }
        return true;    //棋盘已满
    }


    public boolean isForbidden_hands(int row, int col){
        if (check_overfive(row,col)){
            return true;
        }

        int three_Count = 0;
        int four_Count = 0;

        int[][] directions = {{0,1}, {1,0}, {1,1}, {1,-1}};

        for (int[] dir : directions){
            LineInfo info = countLine(row, col, dir[0], dir[1]);

            if (info.three) {
                three_Count++;
            } else if (info.four) {
                four_Count++;
            }

            if (three_Count >= 2 || four_Count >=2){
                return true;
            }
        }

        return false;
    }
    //3，4信息
    public class LineInfo{
        boolean three;
        boolean four;
    }


    public LineInfo countLine(int row, int col, int drow, int dcol){

        LineInfo info = new LineInfo();
        int player = board[row][col];

        int[] positive =count_open(row, col, drow, dcol, player);
        int[] unpositive =count_open(row, col, -drow, -dcol, player);

        int counts = 1 + positive[0] + unpositive[0];
        int opens = positive[1] + unpositive[1];

        if (counts == 3 && opens == 2){
            info.three = true;
        } else if (counts == 4 && opens >= 1) {
            info.four = true;
        }

        return info;
    }


    private int[] count_open(int row, int col, int drow, int dcol, int player) {
        int count = 0;
        int open = 0;
        int r = row + drow;  //水平坐标+水平增量
        int c = col + dcol;

        //从 (row, col) 开始，沿着方向 (drow, dcol) 有多少颗与当前玩家 player 同色的连续棋子
        while (r >= 0 && r < board_Size && c >= 0 && c < board_Size && board[r][c] == player){
            count++;
            r += drow;      //如,检查下一个行的位置
            c += dcol;
        }
        if (r >= 0 && r < board_Size && c >= 0 && c < board_Size && board[r][c] == 0){
            open++;
        }
        return new int[]{count, open};
    }


    public boolean check_overfive(int row, int col){
        int[][] directions = {{0,1}, {1,0}, {1,1}, {1,-1}};

        for (int[] dir : directions) {
            int count = countComponents(row, col, dir[0], dir[1], 1);
            if (count > 5) {
                return true;
            }
        }

        return false;
    }


    public boolean checkWin(int row, int col) {          //方法，判断胜利代码
        int plear = board[row][col];
        int[][] directions = {{0,1}, {1,0}, {1,1}, {1,-1}};

        for (int[] dir : directions){
            if (countComponents(row, col, dir[0],dir[1],plear) >= 5){
                return true;
            }
        }
        return false;
    }

    //计算棋子数量，drow，dcol 方向的增量，如(1,0)水平 (0,1)垂直 (1,1)左上到右下——对角线 || player 1黑 2白
    private int countComponents(int row, int col, int drow, int dcol, int player) {     //方法
        int count = 1;      //当前位置已经有一个棋子

        count += countDirection(row, col, drow, dcol, player);  // 正向检查

        count += countDirection(row, col, -drow, -dcol, player);    // 反向检查

        return count;
    }

    //胜利条件判断（水平/垂直/对角线)
    private int countDirection(int row, int col, int drow, int dcol, int player) {      //方法
        int count = 0;
        int r = row + drow;  //水平坐标+水平增量
        int c = col + dcol;

        //从 (row, col) 开始，沿着方向 (drow, dcol) 有多少颗与当前玩家 player 同色的连续棋子
        while (r >= 0 && r < board_Size && c >= 0 && c < board_Size && board[r][c] == player){
            count++;
            r += drow;      //检查下一个行的位置
            c += dcol;
        }
        return count;
    }


    public void initializeGame(){       //方法，初始化
        board = new int[board_Size][board_Size];
        player_black = true;
        GameOver = false;
        repaint();  //重绘
    }


    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        // 设置抗锯齿
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        //棋盘
        g.setColor(new Color(220,179,92));
        g.fillRect(0,0,getWidth(),getHeight());    //实心矩形

        //网格
        g.setColor(Color.BLACK);
        for (int i = 0; i < board_Size; i++){
            //横
            g.drawLine(margin, margin + i * cell_Size,
                    margin + (board_Size - 1) * cell_Size, margin + i * cell_Size);

            g.drawLine(margin +i * cell_Size, margin,
                    margin +i * cell_Size, margin + (board_Size - 1 ) * cell_Size);
        }

        int dotSize = 8;
        int[] dots = {3, 7, 11}; // 五子棋标准的五个点位置
        for (int row : dots) {
            for (int col : dots) {
                if ((row == 7 && col == 7) || (row == 3 && col == 3) ||
                        (row == 3 && col == 11) || (row == 11 && col == 3) ||
                        (row == 11 && col == 11)) {
                    g.fillOval(margin + col * cell_Size - dotSize / 2,
                            margin + row * cell_Size - dotSize / 2,
                            dotSize, dotSize);
                }
            }
        }

        for (int row = 0; row <board_Size; row++) {
            for (int col = 0; col < board_Size; col++) {
                if (board[row][col] == 1) { // 黑子
                    g.setColor(Color.BLACK);
                    g.fillOval(margin + col * cell_Size - player_Size / 2,
                            margin + row * cell_Size - player_Size / 2,
                            player_Size,player_Size);
                } else if (board[row][col] == 2) { // 白子
                    g.setColor(Color.WHITE);
                    g.fillOval(margin + col * cell_Size - player_Size / 2,
                            margin + row * cell_Size - player_Size / 2,
                            player_Size, player_Size);
                    g.setColor(Color.BLACK);
                    g.drawOval(margin + col * cell_Size - player_Size / 2,
                            margin + row * cell_Size - player_Size / 2,
                            player_Size, player_Size);
                }
            }
        }
        // 显示当前回合
        g.setColor(Color.BLACK);

        String turn = player_black ? "黑棋回合" : "白棋回合";
        g.drawString(turn, margin, margin + board_Size * cell_Size + 30);

        // 如果游戏结束，显示游戏结束提示
        if (GameOver) {
            g.setColor(Color.RED);
            Font gameOver_Font = new Font("微软雅黑", Font.BOLD, 30);
            g.setFont(gameOver_Font);
            g.drawString("游戏结束", margin + 150, margin + board_Size * cell_Size + 30);
        }
    }
}

